﻿// XmlVisualizer
// Copyright (C) 2009  Roman Betík - rbetik@gmail.com

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.Drawing.Printing;
using System.Diagnostics;
using System.Threading;
using System.Globalization;
using System.Configuration;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Collections;
using System.Collections.ObjectModel;
using System.Printing;
using System.Windows.Xps;
using System.IO.Packaging;
using System.Windows.Xps.Packaging;

using XmlVisualizer.Models;
using XmlVisualizer.Controllers;
using XmlVisualizer.DiagramDesigner;
using XmlVisualizer.DiagramDesigner.Controls;
using Tomers.WPF.Localization;
using System.Windows.Xps.Serialization;


namespace XmlVisualizer {

	/// <summary>
	/// Custom commands.
	/// </summary>
	public class MyCommands {
		#region Static fields
		private static RoutedUICommand exit;
		private static RoutedUICommand language;
		private static RoutedUICommand xpathSelection;
		private static RoutedUICommand export;
		private static RoutedUICommand pageSetup;
		private static RoutedUICommand options;
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes MyCommands class.
		/// </summary>
		static MyCommands() {
			// exit command plus shortcut
			InputGestureCollection inputs = new InputGestureCollection();
			inputs.Add(new KeyGesture(Key.Q, ModifierKeys.Control, "Ctrl+Q"));
			exit = new RoutedUICommand("Exit", "Exit", typeof(MyCommands), inputs);

			// language command
			language = new RoutedUICommand("Language", "Language", typeof(MyCommands), null);

			// XPathSelection command
			xpathSelection = new RoutedUICommand("XPathSelection", "XPathSelection", typeof(MyCommands));

			export = new RoutedUICommand("Export", "Export", typeof(MyCommands));

			pageSetup = new RoutedUICommand("PageSetup", "PageSetup", typeof(MyCommands));

			options = new RoutedUICommand("Options", "Options", typeof(MyCommands));
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets the Exit command.
		/// </summary>
		public static RoutedUICommand Exit {
			get { return exit; }
		}

		/// <summary>
		/// Gets the Language command.
		/// </summary>
		public static RoutedUICommand Language {
			get { return language; }
		}

		/// <summary>
		/// XPath selection command.
		/// </summary>
		public static RoutedUICommand XPathSelection {
			get { return xpathSelection; }
		}

		/// <summary>
		/// Export canvas to jpeg command.
		/// </summary>
		public static RoutedUICommand Export {
			get { return export; }
		}

		public static RoutedUICommand PageSetup {
			get { return pageSetup; }
		}

		public static RoutedUICommand Options {
			get { return options; }
		}
		#endregion
	}

	public partial class Document : Window {
		Margins printingMargins = new Margins();

		#region COMMANDS
		/// <summary>
		/// Creates bindings for standard commands.
		/// </summary>
		private void MakeCommandBindings() {
			// New command
			CommandBinding bindingNew = new CommandBinding(ApplicationCommands.New);
			bindingNew.Executed += NewCommand_Executed;
			this.CommandBindings.Add(bindingNew);

			// Open command
			CommandBinding bindingOpen = new CommandBinding(ApplicationCommands.Open);
			bindingOpen.Executed += OpenCommand_Executed;
			this.CommandBindings.Add(bindingOpen);

			// Save command
			CommandBinding bindingSave = new CommandBinding(ApplicationCommands.Save);
			bindingSave.Executed += SaveCommand_Executed;
			bindingSave.CanExecute += new CanExecuteRoutedEventHandler(bindingSave_CanExecute);
			this.CommandBindings.Add(bindingSave);

			// Save As command
			CommandBinding bindingSaveAs = new CommandBinding(ApplicationCommands.SaveAs);
			bindingSaveAs.Executed += SaveAsCommand_Executed;
			this.CommandBindings.Add(bindingSaveAs);

			// Close command
			CommandBinding bindingClose = new CommandBinding(ApplicationCommands.Close);
			bindingClose.Executed += CloseCommand_Executed;
			this.CommandBindings.Add(bindingClose);

			// Exit command
			CommandBinding bindingExit = new CommandBinding(MyCommands.Exit);
			bindingExit.Executed += ExitCommand_Executed;
			this.CommandBindings.Add(bindingExit);

			// Language command
			CommandBinding bindingLanguage = new CommandBinding(MyCommands.Language);
			bindingLanguage.Executed += LanguageCommand_Executed;
			this.CommandBindings.Add(bindingLanguage);

			// XPathSelection command
			CommandBinding bindingXPath = new CommandBinding(MyCommands.XPathSelection);
			bindingXPath.Executed += new ExecutedRoutedEventHandler(bindingXPath_Executed);
			bindingXPath.CanExecute += new CanExecuteRoutedEventHandler(bindingXPath_CanExecute);
			this.CommandBindings.Add(bindingXPath);

			CommandBinding bindingUndo = new CommandBinding(ApplicationCommands.Undo);
			bindingUndo.Executed += new ExecutedRoutedEventHandler(bindingUndo_Executed);
			bindingUndo.CanExecute += new CanExecuteRoutedEventHandler(bindingUndo_CanExecute);
			this.CommandBindings.Add(bindingUndo);

			CommandBinding bindingRedo = new CommandBinding(ApplicationCommands.Redo);
			bindingRedo.Executed += new ExecutedRoutedEventHandler(bindingRedo_Executed);
			bindingRedo.CanExecute += new CanExecuteRoutedEventHandler(bindingRedo_CanExecute);
			this.CommandBindings.Add(bindingRedo);

			CommandBinding bindingExport = new CommandBinding(MyCommands.Export);
			bindingExport.Executed += new ExecutedRoutedEventHandler(bindingExport_Executed);
			this.CommandBindings.Add(bindingExport);

			CommandBinding bindingPrint = new CommandBinding(ApplicationCommands.Print);
			bindingPrint.Executed += new ExecutedRoutedEventHandler(bindingPrint_Executed);
			this.CommandBindings.Add(bindingPrint);

			CommandBinding bindingPageSetup = new CommandBinding(MyCommands.PageSetup);
			bindingPageSetup.Executed += new ExecutedRoutedEventHandler(bindingPageSetup_Executed);
			this.CommandBindings.Add(bindingPageSetup);

			CommandBinding bindingOptions = new CommandBinding(MyCommands.Options);
			bindingOptions.Executed += new ExecutedRoutedEventHandler(bindingOptions_Executed);
			this.CommandBindings.Add(bindingOptions);
		}

		void bindingOptions_Executed(object sender, ExecutedRoutedEventArgs e) {
			Dialogs.DialogSettings dialog = new XmlVisualizer.Dialogs.DialogSettings();
			dialog.Owner = this;
			dialog.ShowModal();
		}

		void bindingPageSetup_Executed(object sender, ExecutedRoutedEventArgs e) {
			System.Windows.Forms.PageSetupDialog PageSetupDialog1 = new System.Windows.Forms.PageSetupDialog();
			PageSetupDialog1.PageSettings = new System.Drawing.Printing.PageSettings();

			PageSetupDialog1.PrinterSettings = new System.Drawing.Printing.PrinterSettings();

			//Do not show the network in the printer dialog.
			PageSetupDialog1.ShowNetwork = false;

			//Show the dialog storing the result.
			System.Windows.Forms.DialogResult result = PageSetupDialog1.ShowDialog();

			PaperSize paperSize = new PaperSize();
			bool landscape;
			string printerName;
			PrintRange printRange;

			if (result == System.Windows.Forms.DialogResult.OK) {
				printingMargins = PageSetupDialog1.PageSettings.Margins;
				paperSize = PageSetupDialog1.PageSettings.PaperSize;
				landscape = PageSetupDialog1.PageSettings.Landscape;
				printerName = PageSetupDialog1.PrinterSettings.PrinterName;
				printRange = PageSetupDialog1.PrinterSettings.PrintRange;
			}
		}

		double MarginToDiu(double margin) {
			return (margin / 100) * 96;
		}

		void bindingPrint_Executed(object sender, ExecutedRoutedEventArgs e) {
			bool? print = false;
			PrintDialog dlg = new PrintDialog();

			Application.Current.Dispatcher.Invoke((Action)(() => {
				dlg.PageRangeSelection = PageRangeSelection.AllPages;
				dlg.UserPageRangeEnabled = true;
				print = dlg.ShowDialog();
			}));

			if (print == true) {
				//get selected printer capabilities
				PrintCapabilities capabilities = null;

				Application.Current.Dispatcher.Invoke((Action)(() => {
					capabilities = dlg.PrintQueue.GetPrintCapabilities(dlg.PrintTicket);
				}));

				//get the size of the printer page
				Size sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);

				double minMarginLeft = capabilities.PageImageableArea.OriginWidth;
				double minMarginTop = capabilities.PageImageableArea.OriginHeight;
				double minMarginRight = (double)capabilities.OrientedPageMediaWidth - capabilities.PageImageableArea.ExtentWidth - capabilities.PageImageableArea.OriginWidth;
				double minMarginBottom = (double)capabilities.OrientedPageMediaHeight - capabilities.PageImageableArea.ExtentHeight - capabilities.PageImageableArea.OriginHeight;

				double marginLeft = Math.Max(MarginToDiu(printingMargins.Left), minMarginLeft);
				double marginTop = Math.Max(MarginToDiu(printingMargins.Top), minMarginTop);
				double marginRight = Math.Max(MarginToDiu(printingMargins.Right), minMarginRight);
				double marginBottom = Math.Max(MarginToDiu(printingMargins.Bottom), minMarginBottom);

				designerCanvas.Margin = new Thickness(marginLeft, marginTop, marginRight, marginBottom);

				designerCanvas.UpdateLayout();
				//get scale of the print wrt to screen of WPF visual
				//double scale = Math.Min((capabilities.PageImageableArea.ExtentWidth - marginLeft - marginRight) / designerCanvas.ActualWidth,
				//	(capabilities.PageImageableArea.ExtentHeight - marginTop - marginBottom) / (designerCanvas.ActualHeight));

				double scale = Math.Min(((double)capabilities.OrientedPageMediaWidth - marginLeft - marginRight) / designerCanvas.ActualWidth,
					((double)capabilities.OrientedPageMediaHeight - marginTop - marginBottom) / (designerCanvas.ActualHeight));

				// save old LayoutTransform
				Transform transform = designerCanvas.LayoutTransform;

				// Transform the designerCanvas to scale
				designerCanvas.LayoutTransform = new ScaleTransform(scale, scale);

				designerCanvas.Measure(sz);
				//designerCanvas.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
				designerCanvas.Arrange(new Rect(new Point(marginLeft, marginTop), new Point((double)capabilities.OrientedPageMediaWidth - marginRight, (double)capabilities.OrientedPageMediaHeight - marginBottom)));
				//designerCanvas.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), new Point(sz.Width - marginRight, sz.Height - marginBottom)));
				designerCanvas.UpdateLayout();

				MemoryStream ms = new MemoryStream();
				Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
				string pack = "pack://temp.xps";

				if (PackageStore.GetPackage(new Uri(pack)) != null) {
					PackageStore.RemovePackage(new Uri(pack));
				}
				PackageStore.AddPackage(new Uri(pack), pkg);
				XpsDocument doc = new XpsDocument(pkg, CompressionOption.SuperFast, pack);
				XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
				writer.Write(designerCanvas);

				Application.Current.Dispatcher.Invoke((Action)(() => {
					FixedDocumentSequence fixedDocSeq = doc.GetFixedDocumentSequence();
					dlg.PrintDocument(fixedDocSeq.DocumentPaginator, "Test");
				}));
				doc.Close();
				designerCanvas.LayoutTransform = transform;
				designerCanvas.Margin = new Thickness(0.0d);
			}
		}

		void bindingExport_Executed(object sender, ExecutedRoutedEventArgs e) {
			System.Windows.Forms.SaveFileDialog saveFileDialog = new System.Windows.Forms.SaveFileDialog();
			saveFileDialog.Title = "Export";
			saveFileDialog.Filter = "JPEG (*.jpg)|*.jpg|BMP (*.bmp)|*.bmp|PNG (*.png)|*.png|TIFF (*.tiff)|*.tiff|GIF (*.gif)|*.gif|XPS *.xps|*.xps";
			saveFileDialog.FilterIndex = 1;
			saveFileDialog.RestoreDirectory = true;

			if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
				string path = saveFileDialog.FileName;
				string extension = Utils.StringUtils.GetExtension(path);

				BitmapEncoder encoder = null;
				if (extension == ".jpg") {
					encoder = new JpegBitmapEncoder();
				} else if (extension == ".bmp") {
					encoder = new BmpBitmapEncoder();
				} else if (extension == ".png") {
					encoder = new PngBitmapEncoder();
				} else if (extension == ".tiff") {
					encoder = new TiffBitmapEncoder();
				} else if (extension == ".gif") {
					encoder = new GifBitmapEncoder();
				} else if (extension == ".xps") {
					encoder = null;
				} else {
					return;
				}

				//designerCanvas.UpdateLayout();

				Transform transform = designerCanvas.LayoutTransform;
				designerCanvas.LayoutTransform = null;
				Size size = new Size(designerCanvas.ActualWidth, designerCanvas.ActualHeight);

				//designerCanvas.Measure(size);
				//designerCanvas.Arrange(new Rect(0, 0, size.Width, size.Height));
				//designerCanvas.UpdateLayout();

				if (encoder != null) {
					//designerCanvas.Measure(size);
					//designerCanvas.Arrange(new Rect(0, 0, size.Width, size.Height));
					designerCanvas.UpdateLayout();

					RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Default);
					renderTargetBitmap.Render(designerCanvas);

					encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

					using (Stream fs = File.Create(path)) {
						encoder.Save(fs);
					}

					designerCanvas.UpdateLayout();
				} else {
					//designerCanvas.Measure(size);
					//designerCanvas.Arrange(new Rect(size));
					designerCanvas.UpdateLayout();

					// Open new package
					Package package = Package.Open(path, FileMode.Create);
					// Create new xps document based on the package opened
					XpsDocument doc = new XpsDocument(package);
					// Create an instance of XpsDocumentWriter for the document
					XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
					// Write the canvas (as Visual) to the document
					writer.Write(designerCanvas);
					// Close document
					doc.Close();
					// Close package
					package.Close();

					designerCanvas.UpdateLayout();
				}

				designerCanvas.LayoutTransform = transform;
			}
		}

		void bindingRedo_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = ApplicationModel.actionManager.CanRedo;
		}

		void bindingUndo_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = ApplicationModel.actionManager.CanUndo;
		}

		void bindingRedo_Executed(object sender, ExecutedRoutedEventArgs e) {
			ApplicationModel.actionManager.Redo();
		}

		void bindingUndo_Executed(object sender, ExecutedRoutedEventArgs e) {
			ApplicationModel.actionManager.Undo();
		}

		// File -> New
		private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			if (e.Parameter is string) {
				string parameter = e.Parameter as string;
				object documentType;
				try {
					documentType = Enum.Parse(typeof(Mode), parameter);
					Mode type = (Mode)documentType;

					ProcessStartInfo psi = new ProcessStartInfo(Process.GetCurrentProcess().MainModule.FileName);
					psi.Arguments = "-mode=" + type.ToString();
					System.Diagnostics.Process.Start(psi);
				} catch (ArgumentException ex) {
					Exceptions.ExceptionHandling.HandleException(ex);
				}
			}
		}

		// File -> Open
		private void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			OpenFile();
		}

		// File -> Save
		private void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			controller.SaveDocument(FileName);
		}

		private void bindingSave_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = Modified;
		}

		// File -> Save As
		private void SaveAsCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			SaveFileDialog();
			//DesignerCanvas.Save.Execute(null, designerCanvas);

			//controller.PrepareForSerialization();
			//DesignerCanvas.Save.Execute(null, designerCanvas);
			//controller.SerializationDone();
		}

		// File -> Close
		private void CloseCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			if (SaveDocumentDialog()) {
				controller.Close();
			}
		}

		// File -> Exit
		private void ExitCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
			this.Close();
		}

		// Language change
		private void LanguageCommand_Executed(Object sender, ExecutedRoutedEventArgs e) {
			if (e.Parameter is String) {
				controller.ChangeLanguage(e.Parameter as String);
			}
		}

		// XPathSelection command
		void bindingXPath_Executed(object sender, ExecutedRoutedEventArgs e) {
			if (e.Parameter is String) {
				controller.SelectNodes(e.Parameter as string);
			} else if (e.Parameter is ComboBox) {
				ComboBox cb = e.Parameter as ComboBox;
				controller.SelectNodes(cb.Text);
				if (!cb.Items.Contains(cb.Text)) {
					cb.Items.Add(cb.Text);
				}
			}

		}

		void bindingXPath_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
			if (this.wellformed)
				e.CanExecute = true;
		}
		#endregion
	}
}